package com.hlkj.warboot.configurer.other;

import com.hlkj.warboot.configurer.security.SecurityUser;
import com.hlkj.warboot.configurer.util.LoggerUtil;
import com.hlkj.warboot.configurer.util.ResponseUtil;
import com.hlkj.warboot.configurer.util.base.BaseExceptionUtil;
import com.hlkj.warboot.module_sys.entity.LogEntity;
import com.hlkj.warboot.module_sys.service.LogService;
import org.springframework.http.HttpStatus;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.ModelAndView;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.text.MessageFormat;
import java.util.Date;

/**
 * @Description TODO 自定义异常捕获器
 * @Author 李星
 * @Date 2019/6/29 22:48
 */
/*
 * 1、Spring Boot默认的异常处理机制：在请求到达controller之前发生错误，会发起 "error/状态码" 请求,所以只需要在template目录下创建error.html文件即可
 *   1.1、浏览器发出来的：返回html代码
 *   1.2、非浏览器发出来的：返回json
 *   详解org.springframework.boot.autoconfigure.web.BasicErrorController
 * 2、通过ControllerAdvice注解自定义全局Controller异常处理器来捕获Controller层抛出的异常
 *   2.1、Restful API请求（@ResponseBody、@RestController注解修饰的Controller）发生错误，输出JSON
 *   2.2、普通页面请求发生错误，输出错误页面
 */
@ControllerAdvice
public class ControllerExceptionHandler {
    @Resource
    private LogService logService;

    /*
     * @Description 捕获所有类型的异常
     * @Author 李星
     * @Date 2019/8/11 15:01
     * @Param [request, response, handlerMethod, e]
     * @return org.springframework.web.servlet.ModelAndView
     */
    @ExceptionHandler(Exception.class)
    public ModelAndView exceptionHandler(HttpServletRequest request, HttpServletResponse response, HandlerMethod handlerMethod, Exception e) {
        String requestUrl = request.getRequestURI(); // 请求的URL
        String requestMethod = request.getMethod(); // 请求方法的类型 GET、POST
        String status = String.valueOf(HttpStatus.INTERNAL_SERVER_ERROR.value()); // 服务器错误码 403、404、500
        String code = "1"; // 异常编码
        String message; // 异常信息

        if (e instanceof BaseExceptionUtil) {
            message = e.getMessage(); // 自定义异常  // code = ((CustomException) e).getCode();
        } else {
            message = "系统错误"; // 系统未知异常
            SecurityUser securityUser; // 当前登录系统的用户信息
            try {
                securityUser = (SecurityUser) SecurityContextHolder.getContext().getAuthentication().getPrincipal();
            } catch (Exception exception) {
                securityUser = null;
            }
            try {
                LogEntity logEntity = new LogEntity();
                logEntity.setCreateTime(new Date()); // 创建时间
                logEntity.setCreateUserAccount(securityUser != null ? securityUser.getUsername() : "null"); // 创建者账号
                logEntity.setCreateUserGrade(securityUser != null ? securityUser.getGrade() : 0); // 创建者等级
                logEntity.setCategory(1); // 日志类型（1:异常日志,2:行为日志）
                logEntity.setContent(e.getMessage()); // 异常内容
                logService.save(logEntity); // 添加日志
            } catch (Exception exception) {
                LoggerUtil.error(MessageFormat.format("日志入库出错：{0}", exception.getMessage()));
            }
        }

        LoggerUtil.error(MessageFormat.format(
                "【全局异常控制器】==========\n服务器错误编码<{0}>：-[{1}]{2}\n自定义异常编码<{3}>：{4}",
                status,
                requestMethod,
                requestUrl,
                code,
                e.fillInStackTrace()));

        boolean isRestfulApi =
                handlerMethod != null &&
                        (handlerMethod.hasMethodAnnotation(ResponseBody.class) || handlerMethod.getBeanType().isAnnotationPresent(RestController.class));
        if (isRestfulApi) {
            ResponseUtil.outputJson(response, ResponseUtil.fail(message));
        } else {
            ModelAndView modelAndView = new ModelAndView();
            modelAndView.addObject("code", code);
            modelAndView.addObject("message", message);
            switch (status) {
                case "403":
                    modelAndView.setViewName("403");
                    break;
                case "404":
                    modelAndView.setViewName("404");
                    break;
                default:
                    modelAndView.setViewName("error");
                    break;
            }
            return modelAndView;
        }
        return null;
    }

}
